package xyz.xtt.gateway;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.server.ServerWebExchange;
import xyz.xtt.gateway.cons.Cons;
import xyz.xtt.gateway.utils.SHA256Utils;

import java.net.URI;
import java.util.Arrays;

/**
 * 鉴名认证
 * 
 * @author huqibo
 *
 */
@Slf4j
@Component
@RefreshScope
public class SignAuthV2GatewayFilter extends AbstractGatewayFilterFactory<SignAuthV2GatewayFilter.Config> {
	private final Environment environment;
	// 平台标记
	private static final String PLATFORM = "platform";

	@Autowired
	public SignAuthV2GatewayFilter(Environment environment) {
		super(Config.class);
		this.environment = environment;
	}

	private final PathMatcher pathMatcher = new AntPathMatcher();

	@Value("${gateway.skipUrl:[]}")
	private String[] skipUrl;

	public static class Config {
		// 过滤器的配置参数
	}

	/**
	 * 检查路径是否在白名单中
	 */
	private boolean checkSkipUrl(String requestUrl) {
		return Arrays.asList(skipUrl).stream().anyMatch(pattern -> pathMatcher.match(pattern, requestUrl));
	}

	@Override
	public GatewayFilter apply(Config config) {
		// 在这里创建并返回过滤器实例
		return (exchange, chain) -> {
			URI uri = exchange.getRequest().getURI();
			String path = exchange.getRequest().getPath().value();
			log.info("SignAuthGatewayFilter path: {}, skipUrl:{}", path, Arrays.toString(skipUrl));
			if (checkSkipUrl(path)) {
				log.info("SignAuthGatewayFilter checkSkipUrl:{}", path);
				return chain.filter(exchange);
			}
			// 业务线标识
			String platform = exchange.getRequest().getHeaders().getFirst(Cons.PLATFORM);
			// 调用方签名值
			String signature = exchange.getRequest().getHeaders().getFirst(Cons.SIGNATURE);
			// 毫秒级时间戳
			String timestamp = exchange.getRequest().getHeaders().getFirst(Cons.TIMESTAMP);
			// 随机字符串，此字段透传给backend服务接口用于实现幂等
			String nonceStr = exchange.getRequest().getHeaders().getFirst(Cons.NONCESTRING);
			if (StringUtils.isBlank(platform)
					|| StringUtils.isBlank(signature)
					|| StringUtils.isBlank(timestamp)
					|| StringUtils.isBlank(nonceStr)
			) {
				exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
				return exchange.getResponse().setComplete();
			}
			// 根据timestamp判断是否过期
			if (System.currentTimeMillis() - Long.parseLong(timestamp) > 1000 * 60 * 5) {
				log.warn("timestamp expired, timestamp: {}", timestamp);
				exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
				return exchange.getResponse().setComplete();
			}

			String accessKey = String.format("auth.%s.accessKey", platform);
			String secretKey = String.format("auth.%s.secretKey", platform);
			String accessValue = environment.getProperty(accessKey);
			String secretValue = environment.getProperty(secretKey);
			log.info("uri: {}, accessKey: {}, accessValue: {}, secretKey: {}, secretValue: {}", uri, accessKey, accessValue, secretKey, secretValue);
			if (StringUtils.isBlank(accessValue) || StringUtils.isBlank(secretValue)) {
				exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
				return exchange.getResponse().setComplete();
			}
			boolean check = SHA256Utils.check(signature, platform, accessValue, secretValue, timestamp, nonceStr);
			if (!check) {
				exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
				return exchange.getResponse().setComplete();
			}
			mutateNewHeader(exchange, platform,nonceStr);
			return chain.filter(exchange);
		};
	}

	private void mutateNewHeader(ServerWebExchange exchange, String platform, String nonceStr) {
		ServerHttpRequest newRequest = exchange.getRequest().mutate()
				.header(PLATFORM, platform)
				.header(Cons.NONCESTRING, nonceStr)
				.build();
		exchange.mutate().request(newRequest).build();
	}
}